package main

import (
	"database/sql"
	"flag"
	"fmt"
	"io"
	"log"
	"os"

	_ "github.com/go-sql-driver/mysql"
)

// 全局变量
var (
	configFile = flag.String("config", "config.yaml", "指定配置文件路径，默认为 config.yaml")
	action     = flag.String("action", "", "指定操作类型: start (启用事件管理器)、stop (停止事件管理器)、init (初始化事件管理器)、drop (删除事件管理器) 或 encrypt (加密配置文件中的密码)")
	host       = flag.String("host", "", "MySQL服务器主机地址（encrypt操作除外，该操作不需要主机地址）")
	port       = flag.Int("port", 3306, "MySQL服务器端口号，默认为 3306")
	force      = flag.Bool("force", false, "强制执行模式，跳过确认提示（仅在 init 和 drop 操作时有效）")
	logFile    = flag.String("log-file", "mysql-event-manager.log", "指定日志输出文件路径，设为 'console' 则仅输出到控制台，默认为 mysql-event-manager.log")
)

func main() {
	flag.Parse()

	// 设置日志
	var multiWriter io.Writer
	if *logFile == "console" {
		// 仅输出到控制台
		multiWriter = os.Stdout
	} else {
		// 同时输出到文件和控制台
		logFile, err := os.OpenFile(*logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			// 如果无法创建日志文件，则仅输出到控制台
			fmt.Fprintf(os.Stderr, "警告: 无法创建日志文件: %v\n", err)
			multiWriter = os.Stdout
		} else {
			defer logFile.Close()
			multiWriter = io.MultiWriter(logFile, os.Stdout)
		}
	}
	log.SetOutput(multiWriter)

	// 检查必需参数
	if *action == "" {
		log.Println("错误: 请指定操作类型: -action=start、-action=stop、-action=init、-action=drop 或 -action=encrypt")
		flag.Usage()
		os.Exit(1)
	}

	if *action != "encrypt" {
		if *host == "" {
			log.Println("错误: 请指定主机地址: -host=hostname")
			flag.Usage()
			os.Exit(1)
		}

		log.Printf("开始执行操作: %s, 主机: %s, 端口: %d", *action, *host, *port)

		// 加载配置文件
		config, err := loadConfig(*configFile)
		if err != nil {
			log.Fatalf("加载配置文件失败: %v", err)
			log.Println("请确保配置文件存在且可读，或使用 -config 参数指定配置文件路径")
			os.Exit(1)
		}

		// 创建数据库连接 (对于init和drop操作，先不指定数据库)
		var dsn string
		if *action == "init" || *action == "drop" {
			dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/",
				config.MySQL.Username,
				config.MySQL.Password,
				*host,
				*port)
		} else {
			dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
				config.MySQL.Username,
				config.MySQL.Password,
				*host,
				*port,
				config.MySQL.DBName)
		}

		db, err := sql.Open("mysql", dsn)
		if err != nil {
			log.Fatalf("创建数据库连接失败: %v", err)
			os.Exit(1)
		}
		defer db.Close()

		// 检查MySQL是否正常运行
		if err := checkMySQLHealth(db); err != nil {
			log.Fatalf("MySQL健康检查失败: %v", err)
			os.Exit(1)
		}

		// 根据操作类型执行相应功能
		var errResult error
		switch *action {
		case "start":
			errResult = startEventManager(db, config)
		case "stop":
			errResult = stopEventManager(db)
		case "init":
			errResult = initEventManager(db, config, *force)
		case "drop":
			errResult = dropEventManager(db, config, *force)
		default:
			log.Fatalf("未知的操作类型: %s", *action)
			flag.Usage()
			os.Exit(1)
		}

		if errResult != nil {
			log.Fatalf("执行操作失败: %v", errResult)
			os.Exit(1)
		}

		log.Printf("操作 %s 执行成功", *action)
	} else {
		// encrypt操作只需要加密密码
		log.Printf("开始执行操作: %s", *action)
		errResult := encryptConfigPassword(*configFile)
		if errResult != nil {
			log.Fatalf("执行操作失败: %v", errResult)
			os.Exit(1)
		}
		log.Printf("操作 %s 执行成功", *action)
	}
}

// encryptConfigPassword 加密配置文件中的密码
func encryptConfigPassword(configFile string) error {
	log.Println("开始加密配置文件中的密码")

	// 加载配置文件
	config, err := loadConfig(configFile)
	if err != nil {
		return fmt.Errorf("加载配置文件失败: %v", err)
	}

	// 检查密码是否已经加密
	if config.MySQL.Password == "" {
		log.Println("配置文件中没有密码需要加密")
		return nil
	}

	// 尝试解密密码以检查是否已加密
	_, err = decryptPassword(config.MySQL.Password)
	if err == nil {
		log.Println("配置文件中的密码似乎已经加密")
		return nil
	}

	// 加密密码并保存配置文件
	err = saveConfig(configFile, config)
	if err != nil {
		return fmt.Errorf("保存加密后的配置文件失败: %v", err)
	}

	log.Println("密码加密完成并已保存到配置文件")
	return nil
}

// checkMySQLHealth 检查MySQL是否正常运行
func checkMySQLHealth(db *sql.DB) error {
	err := db.Ping()
	if err != nil {
		return fmt.Errorf("MySQL健康检查失败: %v", err)
	}
	return nil
}

// checkReadOnlyMode 检查MySQL是否处于只读模式
func checkReadOnlyMode(db *sql.DB) error {
	var readOnly, superReadOnly int
	err := db.QueryRow("SELECT @@read_only, @@super_read_only").Scan(&readOnly, &superReadOnly)
	if err != nil {
		log.Printf("检查MySQL只读状态失败: %v", err)
		return fmt.Errorf("检查MySQL只读状态失败: %v", err)
	}

	if readOnly == 1 || superReadOnly == 1 {
		errMsg := fmt.Sprintf("MySQL服务器当前处于只读模式 (read_only=%d, super_read_only=%d)，无法执行操作", readOnly, superReadOnly)
		log.Println(errMsg)
		return fmt.Errorf(errMsg)
	}

	return nil
}

// startEventManager 启用事件管理器
func startEventManager(db *sql.DB, config *Config) error {
	log.Println("开始启用事件管理器")

	// 检查MySQL是否处于只读模式
	if err := checkReadOnlyMode(db); err != nil {
		return err
	}

	// 获取数据库名
	dbName := config.MySQL.DBName
	if dbName == "" {
		dbName = "rds_meta"
	}

	// 查询表中符合条件的事件，然后将符合条件的事件执行enable event语句（除了ev_sync_event_status）
	rows, err := db.Query(fmt.Sprintf("SELECT db_name, event_name FROM `%s`.ha_event_status WHERE original_status = 'ENABLED' AND event_name != 'ev_sync_event_status'", dbName))
	if err != nil {
		log.Printf("查询事件状态失败: %v", err)
		return fmt.Errorf("查询事件状态失败: %v", err)
	}
	defer rows.Close()

	// 启用所有原始状态为ENABLED的事件（除了ev_sync_event_status）
	count := 0
	failedCount := 0
	for rows.Next() {
		var dbName, eventName string
		err := rows.Scan(&dbName, &eventName)
		if err != nil {
			log.Printf("扫描行数据失败: %v", err)
			continue
		}

		query := fmt.Sprintf("ALTER EVENT `%s`.`%s` ENABLE", dbName, eventName)
		_, err = db.Exec(query)
		if err != nil {
			log.Printf("启用事件 `%s`.`%s` 失败: %v", dbName, eventName, err)
			failedCount++
		} else {
			log.Printf("成功启用事件 `%s`.`%s`", dbName, eventName)
			count++
		}
	}

	if err = rows.Err(); err != nil {
		log.Printf("迭代查询结果失败: %v", err)
		return fmt.Errorf("迭代查询结果失败: %v", err)
	}

	// 最后启用ev_sync_event_status事件
	_, err = db.Exec(fmt.Sprintf("ALTER EVENT `%s`.`ev_sync_event_status` ENABLE", dbName))
	if err != nil {
		log.Printf("启用事件 `%s`.`ev_sync_event_status` 失败: %v", dbName, err)
		failedCount++
	} else {
		log.Printf("成功启用事件 `%s`.`ev_sync_event_status`", dbName)
		count++
	}

	log.Printf("事件管理器启用完成，成功启用 %d 个事件，失败 %d 个事件", count, failedCount)

	// 如果所有事件都启用失败，则返回错误
	if count == 0 && (failedCount > 0) {
		errMsg := "所有事件启用失败"
		log.Println(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 启用事件调度器
	_, err = db.Exec("SET GLOBAL event_scheduler = ON")
	if err != nil {
		log.Printf("启用事件调度器失败: %v", err)
		return fmt.Errorf("启用事件调度器失败: %v", err)
	}
	log.Println("已启用事件调度器")

	return nil
}

// stopEventManager 停止事件管理器
func stopEventManager(db *sql.DB) error {
	log.Println("开始停止事件管理器")

	// 关闭事件调度器
	_, err := db.Exec("SET GLOBAL event_scheduler = OFF")
	if err != nil {
		return fmt.Errorf("关闭事件调度器失败: %v", err)
	}

	log.Println("事件管理器停止完成")
	return nil
}

// dropEventManager 删除事件管理器及相关资源
func dropEventManager(db *sql.DB, config *Config, force bool) error {
	log.Println("开始删除事件管理器对象")

	dbName := config.MySQL.DBName
	if dbName == "" {
		dbName = "rds_meta"
	}

	// 检查MySQL是否处于只读模式
	if err := checkReadOnlyMode(db); err != nil {
		return err
	}

	// 确认删除操作 - 第一次确认
	if !force {
		fmt.Printf("警告: 确定要删除数据库 '%s' 中的EVENT, PROCEDURE和TABLE对象吗? [y/N]: ", dbName)
		var response string
		_, err := fmt.Scanln(&response)
		if err != nil {
			fmt.Println("读取用户输入失败，取消操作")
			return nil
		}

		if response != "y" && response != "Y" {
			fmt.Println("用户取消操作")
			return nil
		}
	} else {
		log.Printf("强制模式: 删除数据库 '%s' 中的EVENT, PROCEDURE和TABLE对象", dbName)
	}

	// 重新连接到指定数据库
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
		config.MySQL.Username,
		config.MySQL.Password,
		*host,
		*port,
		dbName)

	db.Close()
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return fmt.Errorf("重新连接数据库失败: %v", err)
	}
	defer db.Close()

	// 删除事件
	_, err = db.Exec(fmt.Sprintf("DROP EVENT IF EXISTS `%s`.ev_sync_event_status", dbName))
	if err != nil {
		log.Printf("删除事件ev_sync_event_status失败: %v", err)
	}

	// 删除存储过程
	_, err = db.Exec(fmt.Sprintf("DROP PROCEDURE IF EXISTS `%s`.sync_event_status", dbName))
	if err != nil {
		log.Printf("删除存储过程sync_event_status失败: %v", err)
	}

	// 删除表
	_, err = db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`.ha_event_status", dbName))
	if err != nil {
		log.Printf("删除表ha_event_status失败: %v", err)
	}

	log.Printf("已删除数据库 '%s' 中的EVENT, PROCEDURE和TABLE对象", dbName)

	// 确认是否删除数据库 - 第二次确认
	if !force {
		fmt.Printf("是否还要删除数据库 '%s' 本身? [y/N]: ", dbName)
		var response string
		_, err := fmt.Scanln(&response)
		if err != nil {
			fmt.Println("读取用户输入失败，保留数据库")
			log.Printf("保留数据库 '%s'", dbName)
			return nil
		}

		if response != "y" && response != "Y" {
			log.Printf("保留数据库 '%s'", dbName)
			return nil
		}
	} else {
		log.Printf("强制模式: 删除数据库 '%s' 本身", dbName)
	}

	// 重新连接到不指定数据库
	dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/",
		config.MySQL.Username,
		config.MySQL.Password,
		*host,
		*port)

	db.Close()
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return fmt.Errorf("重新连接数据库失败: %v", err)
	}
	defer db.Close()

	// 删除数据库
	_, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS `%s`", dbName))
	if err != nil {
		log.Printf("删除数据库%s失败: %v", dbName, err)
		return err
	}

	log.Printf("成功删除数据库 '%s'", dbName)

	// 关闭事件调度器
	_, err = db.Exec("SET GLOBAL event_scheduler = OFF")
	if err != nil {
		log.Printf("关闭事件调度器失败: %v", err)
	} else {
		log.Println("已关闭事件调度器")
	}

	log.Println("事件管理器对象删除完成")
	return nil
}

// initEventManager 初始化事件管理器
func initEventManager(db *sql.DB, config *Config, force bool) error {
	log.Println("开始初始化事件管理器")

	dbName := config.MySQL.DBName
	if dbName == "" {
		dbName = "rds_meta"
	}

	// 检查MySQL是否处于只读模式
	if err := checkReadOnlyMode(db); err != nil {
		return err
	}

	// 使用SHOW DATABASES检查数据库是否已存在
	rows, err := db.Query("SHOW DATABASES")
	if err != nil {
		return fmt.Errorf("查询数据库列表失败: %v", err)
	}
	defer rows.Close()

	exists := false
	for rows.Next() {
		var database string
		err := rows.Scan(&database)
		if err != nil {
			return fmt.Errorf("扫描数据库名称失败: %v", err)
		}
		if database == dbName {
			exists = true
			break
		}
	}

	if err = rows.Err(); err != nil {
		return fmt.Errorf("迭代查询结果失败: %v", err)
	}

	if exists {
		log.Printf("数据库 '%s' 已存在，将直接使用", dbName)
	} else {
		// 创建存储库
		_, err = db.Exec(fmt.Sprintf("CREATE DATABASE `%s`", dbName))
		if err != nil {
			return fmt.Errorf("创建数据库失败: %v", err)
		}
		log.Printf("已创建数据库: %s", dbName)
	}

	// 重新连接到指定数据库
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s",
		config.MySQL.Username,
		config.MySQL.Password,
		*host,
		*port,
		dbName)

	db.Close()
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return fmt.Errorf("重新连接数据库失败: %v", err)
	}
	defer db.Close()

	// 检查表是否已存在
	var tableExists int
	err = db.QueryRow("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'ha_event_status'", dbName).Scan(&tableExists)
	if err != nil {
		return fmt.Errorf("检查表是否存在失败: %v", err)
	}

	if tableExists > 0 {
		log.Println("ha_event_status表已存在")
	} else {
		// 创建event状态记录表
		createTableSQL := fmt.Sprintf(`
		CREATE TABLE ha_event_status (
			db_name VARCHAR(64) NOT NULL,
			event_name VARCHAR(64) NOT NULL,
			original_status varchar(10) NOT NULL,
			last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
			PRIMARY KEY (db_name, event_name),
			INDEX idx_original_status (original_status)
		)`)
		_, err = db.Exec(createTableSQL)
		if err != nil {
			return fmt.Errorf("创建ha_event_status表失败: %v", err)
		}
		log.Println("已创建ha_event_status表")
	}

	// 检查存储过程是否已存在
	var procExists int
	err = db.QueryRow("SELECT COUNT(*) FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = 'sync_event_status'", dbName).Scan(&procExists)
	if err != nil {
		return fmt.Errorf("检查存储过程是否存在失败: %v", err)
	}

	if procExists > 0 {
		log.Println("sync_event_status存储过程已存在")
	} else {
		// 创建存储过程，用于插入与更新event状态
		createProcedureSQL := fmt.Sprintf(`
		CREATE PROCEDURE sync_event_status()
		BEGIN
		    -- Step 1: 插入或更新现有的事件
			INSERT INTO ha_event_status (db_name, event_name, original_status)
			SELECT EVENT_SCHEMA, EVENT_NAME, STATUS
			FROM information_schema.EVENTS
			WHERE STATUS IN ('ENABLED','DISABLED')
			ON DUPLICATE KEY UPDATE
				original_status = VALUES(original_status);

			-- Step 2: 删除已删除的事件
			DELETE t
			FROM ha_event_status t
			LEFT JOIN (
				SELECT EVENT_SCHEMA, EVENT_NAME
				FROM information_schema.EVENTS
				WHERE STATUS IN ('ENABLED','DISABLED')
			) e
			ON t.db_name = e.EVENT_SCHEMA
			AND t.event_name = e.EVENT_NAME
			WHERE e.EVENT_SCHEMA IS NULL;
		END`)
		_, err = db.Exec(createProcedureSQL)
		if err != nil {
			return fmt.Errorf("创建sync_event_status存储过程失败: %v", err)
		}
		log.Println("已创建sync_event_status存储过程")
	}

	// 启用事件调度器
	_, err = db.Exec("SET GLOBAL event_scheduler = ON")
	if err != nil {
		return fmt.Errorf("启用事件调度器失败: %v", err)
	}
	log.Println("已启用事件调度器")

	// 检查事件是否已存在
	var eventExists int
	err = db.QueryRow("SELECT COUNT(*) FROM information_schema.EVENTS WHERE EVENT_SCHEMA = ? AND EVENT_NAME = 'ev_sync_event_status'", dbName).Scan(&eventExists)
	if err != nil {
		return fmt.Errorf("检查事件是否存在失败: %v", err)
	}

	if eventExists > 0 {
		log.Println("ev_sync_event_status事件已存在")
	} else {
		// 创建job，用于定时调用存储过程去更新状态数据
		createEventSQL := fmt.Sprintf(`
		CREATE EVENT ev_sync_event_status
		ON SCHEDULE EVERY 1 MINUTE
		DO
		BEGIN
			CALL sync_event_status();
		END`)
		_, err = db.Exec(createEventSQL)
		if err != nil {
			return fmt.Errorf("创建ev_sync_event_status事件失败: %v", err)
		}
		log.Printf("已创建ev_sync_event_status事件，数据库: %s", dbName)
	}

	log.Println("事件管理器初始化完成")
	return nil
}
